package cn.btks.backstagemanage.menu.service.impl;

import cn.btks.backstagemanage.menu.dto.MenuDto;
import cn.btks.backstagemanage.menu.pojo.Menu;
import cn.btks.backstagemanage.menu.pojo.MenuRolesDto;
import cn.btks.backstagemanage.menu.repository.MenuBaseRepository;
import cn.btks.backstagemanage.menu.repository.MenuRepository;
import cn.btks.backstagemanage.menu.service.MenuService;
import cn.btks.backstagemanage.role.pojo.Role;
import cn.btks.backstagemanage.user.pojo.User;
import cn.btks.backstagemanage.user.service.UserService;
import cn.btks.commonserver.pojo.PageList;
import cn.btks.commonserver.result.ResultState;
import cn.btks.commonserver.result.StateInfo;
import cn.btks.commonserver.util.sm2.SM2EncDecUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.collections4.CollectionUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author lijun
 * @Date 2021/1/9 14:34
 */
@Service
public class MenuServiceImpl implements MenuService {

    @Autowired
    MenuRepository menuRepository;

    @Autowired
    MenuBaseRepository menuBaseRepository;

    @Autowired
    UserService userService;

    @Override
    public Menu findById(Long id) {
        Optional<Menu> optional = menuRepository.findById(id);
        if (optional.isPresent()) {
            return optional.get();
        }
        return null;
    }

    @Override
    public List<Menu> findAllByParentId(Long parentId) {
        return menuRepository.findAllByParentId(parentId);
    }

    @Override
    public List<Menu> findAll() {
        return menuRepository.findAll();
    }

    @Override
    public List<MenuDto> findAllMenuDto() {
        List<Menu> menuList = menuRepository.findAll();
        List<Menu> rootMenus = new ArrayList<>();
        if (menuList != null && menuList.size() > 0) {
            for (Menu item : menuList) {
                if (item.getParentId() == 0) {
                    rootMenus.add(item);
                }
            }
        }
        List<MenuDto> menuDtoList = new ArrayList<>();
        if (rootMenus.size() > 0) {
            for (Menu item : rootMenus) {
                MenuDto menuDto = recursion(item,menuList);
                menuDtoList.add(menuDto);
                List<MenuDto> children = menuDto.getChildren();
                if(children != null && children.size() > 1){
                    Collections.sort(children, new Comparator<MenuDto>() {
                        @Override
                        public int compare(MenuDto o1, MenuDto o2) {
                            return o1.getMenuOrder() - o2.getMenuOrder();
                        }
                    });
                }
            }
        }
        if(menuDtoList != null && menuDtoList.size() > 0){
            Collections.sort(menuDtoList, new Comparator<MenuDto>() {
                @Override
                public int compare(MenuDto o1, MenuDto o2) {
                    return o1.getMenuOrder() - o2.getMenuOrder();
                }
            });
        }

        return menuDtoList;
    }

    @Override
    public List<MenuDto> findByRoleId(Long roleId) {
        List<Menu> menuList = menuRepository.findAllByRoleId(roleId);
        List<Menu> rootMenus = new ArrayList<>();
        if (menuList != null && menuList.size() > 0) {
            for (Menu item : menuList) {
                if (item.getParentId() == 0) {
                    rootMenus.add(item);
                }
            }
        }
        List<MenuDto> menuDtoList = new ArrayList<>();
        if (rootMenus.size() > 0) {
            for (Menu item : rootMenus) {
                MenuDto menuDto = recursion(item,menuList);
                menuDtoList.add(menuDto);
                List<MenuDto> children = menuDto.getChildren();
                if(children != null && children.size() > 1){
                    Collections.sort(children, new Comparator<MenuDto>() {
                        @Override
                        public int compare(MenuDto o1, MenuDto o2) {
                            return o1.getMenuOrder() - o2.getMenuOrder();
                        }
                    });
                }
            }
        }
        if(menuDtoList != null && menuDtoList.size() > 0){
            Collections.sort(menuDtoList, new Comparator<MenuDto>() {
                @Override
                public int compare(MenuDto o1, MenuDto o2) {
                    return o1.getMenuOrder() - o2.getMenuOrder();
                }
            });
        }

        return menuDtoList;
    }

    @Override
    public List<MenuDto> findByRoleId1(Long roleId) throws IOException {
        List<Menu> menuList = new ArrayList<>();
        String encryptRoleId = SM2EncDecUtils.encrypt(String.valueOf(roleId));
        List<String> encryptMenuIds = menuRepository.findAllMenuIdByRoleId(encryptRoleId);
        if(CollectionUtils.isNotEmpty(encryptMenuIds)){
            List<Long> menuIds = new ArrayList<>();
            for (String encryptMenuId : encryptMenuIds) {
                String decryptMenuId = SM2EncDecUtils.decrypt(encryptMenuId);
                menuIds.add(Long.valueOf(decryptMenuId));
            }
            menuList = menuRepository.findAllByIdList(menuIds);
        }
        List<Menu> rootMenus = new ArrayList<>();
        if (menuList != null && menuList.size() > 0) {
            for (Menu item : menuList) {
                if (item.getParentId() == 0) {
                    rootMenus.add(item);
                }
            }
        }
        List<MenuDto> menuDtoList = new ArrayList<>();
        if (rootMenus.size() > 0) {
            for (Menu item : rootMenus) {
                MenuDto menuDto = recursion(item,menuList);
                menuDtoList.add(menuDto);
                List<MenuDto> children = menuDto.getChildren();
                if(children != null && children.size() > 1){
                    Collections.sort(children, new Comparator<MenuDto>() {
                        @Override
                        public int compare(MenuDto o1, MenuDto o2) {
                            return o1.getMenuOrder() - o2.getMenuOrder();
                        }
                    });
                }
            }
        }
        if(menuDtoList != null && menuDtoList.size() > 0){
            Collections.sort(menuDtoList, new Comparator<MenuDto>() {
                @Override
                public int compare(MenuDto o1, MenuDto o2) {
                    return o1.getMenuOrder() - o2.getMenuOrder();
                }
            });
        }

        return menuDtoList;
    }

    @Override
    public List<MenuDto> findAllByUserId(Long userId) {
        List<MenuDto> menuDtoList = new ArrayList<>();
        User user = userService.findById(userId);
        if(user != null){
            List<Role> roleList = user.getRoles();
            if(roleList != null && roleList.size() > 0){
                for(Role role : roleList){
                    List<MenuDto> menuDtoList1 = findByRoleId(role.getId());
                    if(menuDtoList1 != null && menuDtoList1.size() > 0){
                        menuDtoList.addAll(menuDtoList1);
                    }
                }
            }
        }
        return menuDtoList;
    }

    @Override
    public List<MenuDto> findAllByUserId1(Long userId) throws IOException {
        List<MenuDto> menuDtoList = new ArrayList<>();
        User user = userService.findById(userId);
        if(user != null){
            List<Role> roleList = user.getRoles();
            if(roleList != null && roleList.size() > 0){
                for(Role role : roleList){
                    List<MenuDto> menuDtoList1 = findByRoleId1(role.getId());
                    if(menuDtoList1 != null && menuDtoList1.size() > 0){
                        menuDtoList.addAll(menuDtoList1);
                    }
                }
            }
        }
        return menuDtoList;
    }

    @Override
    public ResultState findPageList(Integer pageNumber, Integer pageSize, String name) {
        PageRequest pageRequest = PageRequest.of(pageNumber - 1, pageSize);
        Specification<Menu> specification = new Specification<Menu>() {
            @Override
            public Predicate toPredicate(Root<Menu> root,
                                         CriteriaQuery<?> criteriaQuery,
                                         CriteriaBuilder criteriaBuilder) {
                List<Predicate> predicates = new ArrayList<>();
                if (!StringUtils.isEmpty(name)) {
                    predicates.add(criteriaBuilder.like(root.get("name"),
                            "%" + name + "%"));
                }
                Predicate[] predicates1 = new Predicate[predicates.size()];
                return criteriaBuilder.and(predicates.toArray(predicates1));
            }
        };
        Page<Menu> configPage = menuRepository.findAll(specification, pageRequest);
        if(configPage.getTotalElements() > 0){
            PageList pageList = new PageList();
            pageList.setResult(configPage.getContent());
            pageList.setPageNum(configPage.getTotalPages());
            pageList.setTotalCount(configPage.getTotalElements());
            return new ResultState(StateInfo.FIND_OK.getCode(), StateInfo.FIND_OK.getMsg(), pageList);
        }
        return new ResultState(StateInfo.FIND_NULL.getCode(), StateInfo.FIND_NULL.getMsg());
    }

    @Override
    public ResultState save(Menu menu) {
        Menu menu1 = menuRepository.save(menu);
        return menu1 != null ? new ResultState(StateInfo.SAVE_OK.getCode(), StateInfo.SAVE_OK.getMsg(), menu1) :
                new ResultState(StateInfo.SAVE_ERROR.getCode(), StateInfo.SAVE_ERROR.getMsg());

    }

    @Override
    public ResultState saveAll(List<Menu> menus) {
        if(menus != null && menus.size() > 0){
            List<Menu> menuList = menuRepository.saveAll(menus);
            return menuList != null ? new ResultState(StateInfo.SAVE_OK.getCode(), StateInfo.SAVE_OK.getMsg(), menuList) :
                    new ResultState(StateInfo.SAVE_ERROR.getCode(), StateInfo.SAVE_ERROR.getMsg());
        }
        return null;
    }

    @Override
    public ResultState update(Menu menu) {
        if(menu.getId() != null){
            Optional<Menu> optional = menuRepository.findById(menu.getId());
            if(optional.isPresent()){
                Menu menu1 = menuRepository.save(menu);
                return menu1 != null ? new ResultState(StateInfo.SAVE_OK.getCode(), StateInfo.SAVE_OK.getMsg(), menu1) :
                        new ResultState(StateInfo.SAVE_ERROR.getCode(), StateInfo.SAVE_ERROR.getMsg());
            }
        }
        return new ResultState(StateInfo.SAVE_ERROR.getCode(), StateInfo.SAVE_ERROR.getMsg());
    }

    @Override
    public ResultState updateMenuRoles(MenuRolesDto menuRolesDto) {
        if(menuRolesDto != null){
            menuBaseRepository.updateMenuRoles(menuRolesDto.getRoleId(),menuRolesDto.getMenuIdList());
            return new ResultState(StateInfo.SAVE_OK.getCode(), StateInfo.SAVE_OK.getMsg());
        }
        return new ResultState(StateInfo.SAVE_ERROR.getCode(), StateInfo.SAVE_ERROR.getMsg());
    }

    @Override
    public ResultState updateMenuRoles1(MenuRolesDto menuRolesDto) throws IOException {
        if(menuRolesDto != null){
            menuBaseRepository.updateMenuRoles1(menuRolesDto.getRoleId(),menuRolesDto.getMenuIdList());
            return new ResultState(StateInfo.SAVE_OK.getCode(), StateInfo.SAVE_OK.getMsg());
        }
        return new ResultState(StateInfo.SAVE_ERROR.getCode(), StateInfo.SAVE_ERROR.getMsg());
    }

    @Override
    public ResultState delete(Long id) {
        Optional<Menu> optional = menuRepository.findById(id);
        if(optional.isPresent()){
            int menuRolesCount = menuRepository.queryCountMenuRoles(id);
            if(menuRolesCount > 0){
                menuRepository.deleteMenuRolesByMenuId(id);
            }
            int result = menuRepository.deleteMenuById(id);
            return result > 0 ? new ResultState(StateInfo.DELETE_OK.getCode(), StateInfo.DELETE_OK.getMsg(), result):
                    new ResultState(StateInfo.DELETE_ERROR.getCode(), StateInfo.DELETE_ERROR.getMsg());
        }
        return new ResultState(StateInfo.DELETE_ERROR_2.getCode(), StateInfo.DELETE_ERROR_2.getMsg());
    }

    @Override
    public ResultState delete1(Long id) throws IOException {
        Optional<Menu> optional = menuRepository.findById(id);
        if(optional.isPresent()){
            int menuRolesCount = menuRepository.queryCountMenuRoles1(SM2EncDecUtils.encrypt(String.valueOf(id)));
            if(menuRolesCount > 0){
                menuRepository.deleteMenuRolesByMenuId1(SM2EncDecUtils.encrypt(String.valueOf(id)));
            }
            int result = menuRepository.deleteMenuById(id);
            return result > 0 ? new ResultState(StateInfo.DELETE_OK.getCode(), StateInfo.DELETE_OK.getMsg(), result):
                    new ResultState(StateInfo.DELETE_ERROR.getCode(), StateInfo.DELETE_ERROR.getMsg());
        }
        return new ResultState(StateInfo.DELETE_ERROR_2.getCode(), StateInfo.DELETE_ERROR_2.getMsg());
    }

    @Override
    public ResultState deleteConnection(Long id) {
        Optional<Menu> optional = menuRepository.findById(id);
        if(optional.isPresent()){
            int menuRolesCount = menuRepository.queryCountMenuRoles(id);
            if(menuRolesCount > 0){
                int result = menuRepository.deleteMenuRolesByMenuId(id);
                return result > 0 ? new ResultState(StateInfo.DELETE_OK.getCode(), StateInfo.DELETE_OK.getMsg(), result):
                        new ResultState(StateInfo.DELETE_ERROR.getCode(), StateInfo.DELETE_ERROR.getMsg());
            }
        }
        return new ResultState(StateInfo.DELETE_ERROR_2.getCode(), StateInfo.DELETE_ERROR_2.getMsg());
    }

    @Override
    public ResultState deleteConnection1(Long id) throws IOException {
        Optional<Menu> optional = menuRepository.findById(id);
        if(optional.isPresent()){
            int menuRolesCount = menuRepository.queryCountMenuRoles1(SM2EncDecUtils.encrypt(String.valueOf(id)));
            if(menuRolesCount > 0){
                int result = menuRepository.deleteMenuRolesByMenuId1(SM2EncDecUtils.encrypt(String.valueOf(id)));
                return result > 0 ? new ResultState(StateInfo.DELETE_OK.getCode(), StateInfo.DELETE_OK.getMsg(), result):
                        new ResultState(StateInfo.DELETE_ERROR.getCode(), StateInfo.DELETE_ERROR.getMsg());
            }
        }
        return new ResultState(StateInfo.DELETE_ERROR_2.getCode(), StateInfo.DELETE_ERROR_2.getMsg());
    }

    @Override
    public ResultState test() throws IOException {
        List<Menu> menuList = new ArrayList<>();
        String encryptRoleId = SM2EncDecUtils.encrypt(String.valueOf(1));
        List<Menu> menus = menuRepository.findAll();
        List<Long> collect = menus.stream().map(Menu::getId).collect(Collectors.toList());
        MenuRolesDto menuRolesDto = new MenuRolesDto();
        menuRolesDto.setRoleId(Long.valueOf(1));
        menuRolesDto.setMenuIdList(collect);
        try {
            menuBaseRepository.saveMenuRoles(menuRolesDto.getRoleId(),menuRolesDto.getMenuIdList());
            return new ResultState(StateInfo.SAVE_OK.getCode(), StateInfo.SAVE_OK.getMsg());
        } catch (Exception e){
            return new ResultState(StateInfo.SAVE_ERROR.getCode(), StateInfo.SAVE_ERROR.getMsg());
        }
    }

    /**
     * 递归查询资源清单
     *
     * @param menu
     * @return
     */
    private MenuDto recursion(Menu menu,List<Menu> menuListRoot) {
        Optional<Menu> optional = menuListRoot.stream().filter(s -> s.getId().equals(menu.getId())).findFirst();
        if(!optional.isPresent()){
            return null;
        }
        MenuDto menuDto = new MenuDto();
        menuDto.setParent(menu);
        menuDto.setMenuId(menu.getId());
        menuDto.setName(menu.getName());
        menuDto.setMenuOrder(menu.getMenuOrder());
        menuDto.setUnitElement(menu.getUnitElement());
        menuDto.setMenuName(menu.getMenuName());
        menuDto.setMenuLevel(menu.getMenuLevel());
        menuDto.setIsResource(menu.getIsResource());
        List<MenuDto> children = new ArrayList<>();
        menuDto.setChildren(children);
        Long id = menu.getId();
        List<Menu> menuList = menuRepository.findAllByParentId(id);
        if (menuList != null && menuList.size() > 0) {
            for (Menu item : menuList) {
                MenuDto childrenItem = recursion(item,menuListRoot);
                if(childrenItem != null){
                    children.add(childrenItem);
                }
            }
        }
        return menuDto;
    }
}
